package org.xmap.ui.control;

import java.util.Vector;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;

import org.xmap.app.Env;
import org.xmap.ui.ViewManager;
import org.xmap.ui.base.Event;
import org.xmap.ui.base.Window;
import org.xmap.ui.listener.ItemListener;

/**
 * <p>基础数据结构，用于存放一个列表项。</p>
 * @author Zealot
 * @version 1.0
 */
class ListItem {

    public int id;
    public Object obj;
    public Object first1;
    public Object first2;
    public Object second1;
    public Object second2;
    public Object third1;
    public Object third2;
    public boolean isHighlight;
    public Object objReserv;

    ListItem(int id, Object obj,
             Object first1, Object first2,
             Object second1, Object second2,
             Object third1, Object third2) {
        this.id = id;
        this.obj = obj;
        this.first1 = first1;
        this.first2 = first2;
        this.second1 = second1;
        this.second2 = second2;
        this.third1 = third1;
        this.third2 = third2;
        this.isHighlight = false;
    }

    ListItem(int id, Object obj,
             Object first1, Object first2,
             Object second1, Object second2,
             Object third1, Object third2, boolean isHighlight) {
        this.id = id;
        this.obj = obj;
        this.first1 = first1;
        this.first2 = first2;
        this.second1 = second1;
        this.second2 = second2;
        this.third1 = third1;
        this.third2 = third2;
        this.isHighlight = isHighlight;
    }
}


/**
 * <p>列表基类，所有的列表都从该类派生。</p>
 * @author Zealot
 * @version 1.0
 */
public abstract class ListBase extends Window {

    /**
     * 列表控件的拉伸方式。
     */
    protected int scretchMode;
    /**
     * 存储列表项的内容。
     */
    protected Vector content;
    /**
     * 列表的标题高度。
     */
    protected int titleHeight;
    /**
     * 列表项显示的可用高度。
     */
    protected int viewHeight;
    /**
     * 当前选中的位置。
     */
    protected int index;
    /**
     * 第一个显示的内容的位置。
     */
    protected int drawBase;
    /**
     * 列表区域能够显示几个列表项。
     */
    protected int disCount;
    /**
     * 纵方向的显示偏移。
     */
    protected int disOffset;
    /**
     * 一行占用的高度，单位为像素。
     */
    protected int spacing;
    /**
     * 是否显示标题。
     */
    protected boolean showTitle;
    /**
     * 是否显示列表前面的数字。
     */
    protected boolean showNumber;
    /**
     * 是否启用调整上下列表。
     */
    protected boolean adjust;
    /**
     * 边框的颜色。
     */
//    protected int lineColor;
    /**
     * 选中项的背景颜色。
     */
//    protected int selectedColor;
    /**
     * 标准状态下的文字的颜色。
     */
//    protected int textColor;
    /**
     * 高亮状态下的文字的颜色。
     */
//    protected int highLightTextColor;
    /**
     * 标题一的名字。
     */
    protected String titleName1;
    /**
     * 标题二的名字。
     */
    protected String titleName2;
    /**
     * 标题三的名字。
     */
    protected String titleName3;
    /**
     * 标题一的横方向偏移。
     */
    protected int titleX1;
    /**
     * 标题二的横方向偏移。
     */
    protected int titleX2;
    /**
     * 标题三的横方向偏移。
     */
    protected int titleX3;
    /**
     * 第一项的横方向偏移。
     */
    protected int columnX1;
    /**
     * 第二项的横方向偏移。
     */
    protected int columnX2;
    /**
     * 第三项的横方向偏移。
     */
    protected int columnX3;
    /**
     * 标题一的锚点类型，默认左对齐。
     */
    protected int titleAnchor1;
    /**
     * 标题二的锚点类型，默认居中对齐。
     */
    protected int titleAnchor2;
    /**
     * 标题三的锚点类型，默认右对齐。
     */
    protected int titleAnchor3;
    /**
     * 第一项的锚点类型，默认左对齐。
     */
    protected int columnAnchor1;
    /**
     * 第二项的锚点类型，默认居中对齐。
     */
    protected int columnAnchor2;
    /**
     * 第三项的锚点类型，默认右对齐。
     */
    protected int columnAnchor3;
    /**
     * 触笔选中的序号。
     */
    protected int pressSelectedIndex;
    /**
     * 选项被选中监听器。
     */
    protected ItemListener itemListener;
    /**
     * 一个元素在滚动栏上对应的高度，用于计算滚动条的位置。
     */
    protected int cellHeightInStrollbar;

    /**
     * 指定锚点横坐标，纵坐标，列表宽度，列表高度和锚点类型的构造函数。
     * @param x int 锚点横坐标。
     * @param y int 锚点纵坐标。
     * @param width int 列表宽度。
     * @param height int 列表高度。
     * @param orient int 锚点类型。
     */
    public ListBase(int x, int y, int width, int height, int orient) {
        super(x, y, width, height, orient);
        super.setCanFocus(true);
        this.scretchMode = 0;
        this.content = new Vector();
        this.titleHeight = 0;
        this.viewHeight = height;
        this.index = -1;
        this.drawBase = 0;
        this.disCount = 0;
        this.disOffset = 0;
        this.spacing = Env.instance.defaultFontHeight;
        this.showTitle = false;
        this.showNumber = true;
        this.adjust = true;
//        this.lineColor = Theme.instance.defaultLineColor;
//        this.selectedColor = Theme.instance.defaultSelectedColor;
//        this.textColor = 0xff000000;
//        this.highLightTextColor = 0xffff0000;
        this.titleName1 = "第一列";
        this.titleName2 = "第二列";
        this.titleName3 = "第三列";
        this.titleX1 = 10;
        this.titleX2 = width >> 1;
        this.titleX3 = width - 10;
        this.columnX1 = 12;
        this.columnX2 = width >> 1;
        this.columnX3 = width - 10;
        this.titleAnchor1 = Window.ALIGN_LEFT;
        this.titleAnchor2 = Window.ALIGN_CENTER;
        this.titleAnchor3 = Window.ALIGN_RIGHT;
        this.columnAnchor1 = Window.ALIGN_LEFT;
        this.columnAnchor2 = Window.ALIGN_CENTER;
        this.columnAnchor3 = Window.ALIGN_RIGHT;
        this.pressSelectedIndex = -1;
        this.itemListener = null;
        this.calcuateView();
    }

    /**
     * 设置列表的窗口区域。
     * @param leftUpX int 文本区域左上角横坐标。
     * @param leftUpY int 文本区域左上角纵坐标。
     * @param width int 文本区域宽度。
     * @param height int 文本区域高度。
     */
    public void setRect(int leftUpX, int leftUpY, int width, int height) {
        super.setRect(leftUpX, leftUpY, width, height);
        this.calcuateView();
    }

    /**
     * 屏幕发生改变的通知。
     * @param width int 新的屏幕宽度。
     * @param height int 新的屏幕高度。
     */
    public void sizeChanged(int width, int height) {
        if ((this.scretchMode & Window.SCRETCH_ROW) > 0) { // 横向扩展
            this.getRect().leftUp.x = 0;
            this.getRect().width = width;
        }
        if ((this.scretchMode & Window.SCRETCH_COLUMN) > 0) { // 纵向扩展
            this.getRect().leftUp.y = 0;
            if (this.owner != null) {
                this.getRect().height = this.owner.getClientRect().height;
            }
            this.calcuateView();
        }
    }

    /**
     * 设置拉伸模式。
     * @param scretchMode int 拉伸模式。
     */
    public void setScretchMode(int scretchMode) {
        if (scretchMode <= 3) {
            this.scretchMode = scretchMode;
            if (this.scretchMode != 0) {
                this.sizeChanged(Env.instance.screenWidth, Env.instance.screenHeight);
            }
        }
    }

    /**
     * 获得拉伸模式。
     * @return int 拉伸模式。
     */
    public int getScretchMode() {
        return this.scretchMode;
    }

    /**
     * 添加列表项到列表中。
     * @param id int 列表项被选中的时候，将会在回调时，作为第一个参数。
     * @param obj Object 列表项被选中的时候，将会在回调时，作为第二个参数。
     * @param first1 Object 列表项第一列的第一个参数。
     * @param first2 Object 列表项第一列的第二个参数。
     * @param second1 Object 列表项第二列的第一个参数。
     * @param second2 Object 列表项第二列的第二个参数。
     * @param third1 Object 列表项第三列的第一个参数。
     * @param third2 Object 列表项第三列的第二个参数。
     * @param isHighlight boolean 该列表项是否高亮。
     */
    public void addContent(int id, Object obj,
                           Object first1, Object first2,
                           Object second1, Object second2,
                           Object third1, Object third2, boolean isHighlight) {
        ListItem i = new ListItem(id, obj, first1, first2, second1, second2, third1, third2, isHighlight);
        this.content.addElement(i);
        if (this.index == -1) {
            this.index = 0;
            this.pressSelectedIndex = index;
        }
        if (this.content.size() == 1) {
            this.notifyItemListener();
        }
        this.cellHeightInStrollbar = (this.getRect().height - 2) / this.content.size();
    }

    /**
     * 插入列表项到列表的指定位置中。
     * @param index int 插入的位置。
     * @param id int 列表项被选中的时候，将会在回调时，作为第一个参数。
     * @param obj Object 列表项被选中的时候，将会在回调时，作为第二个参数。
     * @param first1 Object 列表项第一列的第一个参数。
     * @param first2 Object 列表项第一列的第二个参数。
     * @param second1 Object 列表项第二列的第一个参数。
     * @param second2 Object 列表项第二列的第二个参数。
     * @param third1 Object 列表项第三列的第一个参数。
     * @param third2 Object 列表项第三列的第二个参数。
     * @param isHighlight boolean 该列表项是否高亮。
     */
    public void insertContent(int index, int id, Object obj,
                              Object first1, Object first2,
                              Object second1, Object second2,
                              Object third1, Object third2, boolean isHighlight) {
        ListItem i = new ListItem(id, obj, first1, first2, second1, second2, third1, third2, isHighlight);
        this.content.insertElementAt(i, index);
        if (this.index == -1) {
            this.index = 0;
            this.pressSelectedIndex = index;
        }
        this.notifyItemListener();
        this.cellHeightInStrollbar = (this.getRect().height - 2) / this.content.size();
    }

    /**
     * 移除所有的列表项。
     */
    public void removeAllContent() {
        this.index = -1;
        this.pressSelectedIndex = index;
        this.drawBase = 0;
        this.content.removeAllElements();
    }

    /**
     * 移除当前选中的列表项。
     */
    public void removeSelected() {
        if (this.index >= 0) {
            this.content.removeElementAt(this.drawBase + this.index);
            if (this.content.size() == 0) {
                this.index = -1;
            } else if (this.index > this.content.size() - 1) {
                this.index--;
            } else if (this.index == this.disCount - 1) {
                if (this.drawBase > 0) {
                    this.drawBase--;
                } else {
                    this.index--;
                }
            }
            this.pressSelectedIndex = index;
            this.notifyItemListener();
            if (this.content.size() > 0) {
                this.cellHeightInStrollbar = (this.getRect().height - 2) / this.content.size();
            }
        }
    }

    /**
     * 设置是否选中标题。
     * @param showTitle boolean 是否选中标题。
     */
    public void setShowTitle(boolean showTitle) {
        if (showTitle == this.showTitle) {
            return;
        }
        this.showTitle = showTitle;
        this.calcuateView();
    }

    /**
     * 获得是否选中标题。
     * @return boolean 是否选中标题。
     */
    public boolean getShowTitle() {
        return this.showTitle;
    }

    public void setAdjust(boolean adjust) {
        this.adjust = adjust;
    }

    public boolean getAdjust() {
        return this.adjust;
    }

    public void setShowNumber(boolean showNumber) {
        this.showNumber = showNumber;
    }

    public boolean getShowNumber() {
        return this.showNumber;
    }

    public void setColumnName(String first, String second, String third) {
        this.titleName1 = first;
        this.titleName2 = second;
        this.titleName3 = third;
    }

    public void setTitleX(int tx1, int tx2, int tx3) {
        this.titleX1 = tx1;
        this.titleX2 = tx2;
        this.titleX3 = tx3;
    }

    public void setColumnX(int cx1, int cx2, int cx3) {
        this.columnX1 = cx1;
        this.columnX2 = cx2;
        this.columnX3 = cx3;
    }

    public void setTitleAnchor(int ta1, int ta2, int ta3) {
        this.titleAnchor1 = ta1;
        this.titleAnchor2 = ta2;
        this.titleAnchor3 = ta3;
    }

    public void setColumnAnchor(int ca1, int ca2, int ca3) {
        this.columnAnchor1 = ca1;
        this.columnAnchor2 = ca2;
        this.columnAnchor3 = ca3;
    }

    public boolean setFocus(boolean focus) {
        if (this.getCanFocus()) {
            super.setFocus(focus);
            if (focus) {
                this.notifyItemListener();
            }
            return true;
        }
        return false;
    }

    public int getSelectId() {
        if (this.index >= 0) {
            ListItem item = (ListItem)this.content.elementAt(this.index + this.drawBase);
            return item.id;
        } else {
            return -1;
        }
    }

    public Object getSelectObj() {
        if (this.index >= 0) {
            ListItem item = (ListItem)this.content.elementAt(this.index + this.drawBase);
            return item.obj;
        } else {
            return null;
        }
    }

    public ListItem getSelectListItem() {
        if (this.index >= 0) {
            ListItem item = (ListItem)this.content.elementAt(this.index + this.drawBase);
            return item;
        } else {
            return null;
        }
    }

    public int getDisplayIndex() {
        return this.index;
    }

    public int getDisplayCount() {
        return this.disCount;
    }

    public void setDisplayIndex(int index) {
        if (index >= 0) {
            if (this.content.size() <= this.disCount) {
                this.drawBase = 0;
                this.index = Math.min(this.content.size() - 1, index);
            } else {
                if (this.drawBase + index > this.content.size()) {
                    this.index = this.disCount - 1;
                    this.drawBase = index + 1 - this.disCount;
                } else {
                    this.index = index;
                }
            }
            this.pressSelectedIndex = index;
        }
    }

    public int getSelectIndex() {
        return this.index + this.drawBase;
    }

    public int getTitleHeight() {
        return this.titleHeight;
    }

    public void setSelectIndex(int index) {
        if (index >= this.content.size()) {
            index = this.content.size() - 1;
        }
        if (index < 0) {
            index = 0;
        }
        if (this.content.size() <= this.disCount) {
            this.drawBase = 0;
            this.index = Math.min(this.content.size() - 1, index);
        } else {
            if (index >= this.disCount) {
                this.index = this.disCount - 1;
                this.drawBase = index + 1 - this.disCount;
            } else {
                this.drawBase = 0;
                this.index = index;
            }
        }
        if (this.viewHeight % this.spacing != 0) {
            if (this.index == 0) {
                this.disOffset = 0;
            } else if (this.index == (this.disCount - 1)) {
                if (!this.showTitle) {
                    this.disOffset = -(this.spacing - (this.viewHeight % this.spacing));
                }
            }
        }
        this.pressSelectedIndex = index;
        this.notifyItemListener();
    }

    public int getCount() {
        return this.content.size();
    }

    public void setHightLight(int index, boolean isHighlight) {
        if (index >= 0 && index < this.content.size()) {
            ListItem item = (ListItem)this.content.elementAt(index);
            item.isHighlight = isHighlight;
        }
    }

    public void setItemListener(ItemListener il) {
        this.itemListener = il;
        this.notifyItemListener();
    }

    public void setRowHeight(int rowHeight) {
        if (rowHeight > 0 && this.spacing < this.getRect().height) {
            this.spacing = rowHeight;
            this.calcuateView();
        }
    }

    public int getRowHeight() {
        return this.spacing;
    }

//    public void setLineColor(int lineColor) {
//        this.lineColor = lineColor;
//    }

//    public void setSelectedColor(int selectedColor) {
//        this.selectedColor = selectedColor;
//    }

    public boolean keyEvent(boolean flag, int keyCode, int gameAction) {
        if (flag) {
            switch (gameAction) {
            case Canvas.UP:
                this.upSelectIndex();
                return true;
            case Canvas.DOWN:
                this.downSelectIndex();
                return true;
            case Canvas.FIRE:
                ListItem li = this.getSelectListItem();
                if (li != null && this.itemListener != null) {
                    this.itemListener.fireSelected(li.id, li.obj);
                    return true;
                }
            }
        }
        return false;
    }

    public boolean pointerEvent(Event ev) {
        boolean result = false;
        switch (ev.type) {
        case Event.POINTER_PRESSED:
            if (this.getRect().collideWith(ev.param1, ev.param2)) {
                super.setPointerSelected(true);
                this.pointerPressedY = ev.param2; // 记录触笔的垂直位置
                result = true;
            }
            break;
        case Event.POINTER_RELEASED:
            if (super.getPointerSelected()) {
                super.setPointerSelected(false);
                result = true;
            }
            break;
        case Event.POINTER_DRAGGED:
            if (super.getPointerSelected()) {
                if (this.getRect().collideWith(ev.param1, ev.param2)) {
                    int y = ev.param2 - this.pointerPressedY;
                    if (Math.abs(y) > (this.spacing >> 1)) { // 触笔上下移动
                        this.pointerPressedY = ev.param2;
                        if (y > 0) {
                            this.upOper();
                        } else {
                            this.downOper();
                        }
                    }
                    result = true;
                }
            }
            break;
        }
        return result;
    }

    public void upSelectIndex() {
        if (this.index > 0) {
            this.index--;
            if (this.index == 0) {
                this.disOffset = 0;
            }
        } else if (this.drawBase > 0) {
            this.drawBase--;
        } else {
            if (this.content.size() >= this.disCount) {
                this.index = this.disCount - 1;
                this.drawBase = this.content.size() - this.index - 1;
                if (!this.showTitle) {
//                    if (this.getRect().height % this.spacing != 0) {
                    this.disOffset = -(this.spacing - (this.viewHeight % this.spacing));
//                    }
                }
            } else {
                this.index = this.content.size() - 1;
                this.drawBase = 0;
            }
        }
        this.pressSelectedIndex = index;
        this.notifyItemListener();
        ViewManager.instance.requestRepaint();
    }

    public void downSelectIndex() {
        if (this.index < Math.min(this.disCount, this.content.size() - this.drawBase) - 1) {
            this.index++;
            if (this.index == this.disCount - 1) {
                if (!this.showTitle) {
//                    if (this.getRect().height % this.spacing != 0) {
                    this.disOffset = -(this.spacing - (this.viewHeight % this.spacing));
//                    }
                }
            }
        } else if (this.drawBase + this.index < this.content.size() - 1) {
            this.drawBase++;
        } else {
            if (this.content.size() > 0) {
                this.drawBase = 0;
                this.index = 0;
                this.disOffset = 0;
            } else {
                this.drawBase = 0;
                this.index = -1;
            }
        }
        this.pressSelectedIndex = index;
        this.notifyItemListener();
        ViewManager.instance.requestRepaint();
    }

    public void upOper() {
        if (this.drawBase > 0) {
            this.drawBase--;
            this.notifyItemListener();
            ViewManager.instance.requestRepaint();
        }
    }

    public void downOper() {
        if (this.drawBase < this.content.size() - this.disCount) {
            this.drawBase++;
            this.notifyItemListener();
            ViewManager.instance.requestRepaint();
        }
    }

    protected void notifyItemListener() {
        if (this.itemListener != null && super.getFocus()) {
            if (this.index != -1) {
                ListItem li = (ListItem)this.content.elementAt(this.index + this.drawBase);
                if (li != null) {
                    this.itemListener.keySelected(li.id, li.obj);
                }
            }
        }
    }

    private void calcuateView() {
        this.titleX2 = this.getRect().width >> 1;
        this.titleX3 = this.getRect().width - 10;
        this.columnX2 = this.getRect().width >> 1;
        this.columnX3 = this.getRect().width - 10;
        if (this.content.size() > 0) {
            this.index = 0;
        } else {
            this.index = -1;
        }
        this.pressSelectedIndex = index;
        if (this.showTitle) {
            this.titleHeight = this.spacing + this.getRect().height % this.spacing;
        } else {
            this.titleHeight = 0;
        }
        this.viewHeight = this.getRect().height - this.titleHeight;
        this.disCount = this.viewHeight / this.spacing;
        if (this.viewHeight % this.spacing != 0) {
            this.disCount++;
        }
        this.drawBase = 0;
        this.disOffset = 0;
    }

    protected int getMidpAnchor(int anchor) {
        switch (anchor) {
        case Window.ALIGN_LEFT:
            return Graphics.LEFT;
        case Window.ALIGN_CENTER:
            return Graphics.HCENTER;
        case Window.ALIGN_RIGHT:
            return Graphics.RIGHT;
        default:
            return Graphics.LEFT;
        }
    }
}
